<?php

namespace youmbe\phpword\version;

use Yii;
use yii\helpers\ArrayHelper;
use yii\base\InvalidConfigException;
use yii\base\InvalidParamException;
use yii\i18n\Formatter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Style\ListItem as ListItemStyle;
use PhpOffice\Common\Font;
use PhpOffice\PhpWord\SimpleType\LineSpacingRule;
use PhpOffice\PhpWord\SimpleType\TextAlignment;
use PhpOffice\PhpWord\SimpleType\Jc;
use PhpOffice\PhpWord\Style\TablePosition;
use youmbe\phpword\widgets\DetailView;
use PhpOffice\PhpWord\Shared\Html as PhpWordHtml;

/**
 * Word Widget for generate Word File or for load Word File.
 *
 * @author emhome <emhome@163.com>
 * @since 1.0
 */
class Word extends \yii\base\Widget {

//    use WordRenderTrait;
    // Border style
    const BORDER_NONE = 'none';
    const BORDER_DASHDOT = 'dashDot';
    const BORDER_DASHDOTDOT = 'dashDotDot';
    const BORDER_DASHED = 'dashed';
    const BORDER_DOTTED = 'dotted';
    const BORDER_DOUBLE = 'double';
    const BORDER_HAIR = 'hair';
    const BORDER_MEDIUM = 'medium';
    const BORDER_MEDIUMDASHDOT = 'mediumDashDot';
    const BORDER_MEDIUMDASHDOTDOT = 'mediumDashDotDot';
    const BORDER_MEDIUMDASHED = 'mediumDashed';
    const BORDER_SLANTDASHDOT = 'slantDashDot';
    const BORDER_THICK = 'thick';
    const BORDER_THIN = 'thin';
    // Colors
    const COLOR_BLACK = 'FF000000';
    const COLOR_WHITE = 'FFFFFFFF';
    const COLOR_RED = 'FFFF0000';
    const COLOR_DARKRED = 'FF800000';
    const COLOR_BLUE = 'FF0000FF';
    const COLOR_DARKBLUE = 'FF000080';
    const COLOR_GREEN = 'FF00FF00';
    const COLOR_DARKGREEN = 'FF008000';
    const COLOR_YELLOW = 'FFFFFF00';
    const COLOR_DARKYELLOW = 'FF808000';
    // Horizontal alignment styles
    const HORIZONTAL_GENERAL = 'general';
    const HORIZONTAL_LEFT = 'left';
    const HORIZONTAL_RIGHT = 'right';
    const HORIZONTAL_CENTER = 'center';
    const HORIZONTAL_CENTER_CONTINUOUS = 'centerContinuous';
    const HORIZONTAL_JUSTIFY = 'justify';
    const HORIZONTAL_FILL = 'fill';
    const HORIZONTAL_DISTRIBUTED = 'distributed'; // Word2007 only
    // Vertical alignment styles
    const VERTICAL_BOTTOM = 'bottom';
    const VERTICAL_TOP = 'top';
    const VERTICAL_CENTER = 'center';
    const VERTICAL_JUSTIFY = 'justify';
    const VERTICAL_DISTRIBUTED = 'distributed'; // Word2007 only
    // Read order
    const READORDER_CONTEXT = 0;
    const READORDER_LTR = 1;
    const READORDER_RTL = 2;
    // Fill types
    const FILL_NONE = 'none';
    const FILL_SOLID = 'solid';
    const FILL_GRADIENT_LINEAR = 'linear';
    const FILL_GRADIENT_PATH = 'path';
    const FILL_PATTERN_DARKDOWN = 'darkDown';
    const FILL_PATTERN_DARKGRAY = 'darkGray';
    const FILL_PATTERN_DARKGRID = 'darkGrid';
    const FILL_PATTERN_DARKHORIZONTAL = 'darkHorizontal';
    const FILL_PATTERN_DARKTRELLIS = 'darkTrellis';
    const FILL_PATTERN_DARKUP = 'darkUp';
    const FILL_PATTERN_DARKVERTICAL = 'darkVertical';
    const FILL_PATTERN_GRAY0625 = 'gray0625';
    const FILL_PATTERN_GRAY125 = 'gray125';
    const FILL_PATTERN_LIGHTDOWN = 'lightDown';
    const FILL_PATTERN_LIGHTGRAY = 'lightGray';
    const FILL_PATTERN_LIGHTGRID = 'lightGrid';
    const FILL_PATTERN_LIGHTHORIZONTAL = 'lightHorizontal';
    const FILL_PATTERN_LIGHTTRELLIS = 'lightTrellis';
    const FILL_PATTERN_LIGHTUP = 'lightUp';
    const FILL_PATTERN_LIGHTVERTICAL = 'lightVertical';
    const FILL_PATTERN_MEDIUMGRAY = 'mediumGray';
    // Pre-defined formats
    const FORMAT_GENERAL = 'General';
    const FORMAT_TEXT = '@';
    const FORMAT_NUMBER = '0';
    const FORMAT_NUMBER_00 = '0.00';
    const FORMAT_NUMBER_COMMA_SEPARATED1 = '#,##0.00';
    const FORMAT_NUMBER_COMMA_SEPARATED2 = '#,##0.00_-';
    const FORMAT_PERCENTAGE = '0%';
    const FORMAT_PERCENTAGE_00 = '0.00%';
    const FORMAT_DATE_YYYYMMDD2 = 'yyyy-mm-dd';
    const FORMAT_DATE_YYYYMMDD = 'yy-mm-dd';
    const FORMAT_DATE_DDMMYYYY = 'dd/mm/yy';
    const FORMAT_DATE_DMYSLASH = 'd/m/yy';
    const FORMAT_DATE_DMYMINUS = 'd-m-yy';
    const FORMAT_DATE_DMMINUS = 'd-m';
    const FORMAT_DATE_MYMINUS = 'm-yy';
    const FORMAT_DATE_XLSX14 = 'mm-dd-yy';
    const FORMAT_DATE_XLSX15 = 'd-mmm-yy';
    const FORMAT_DATE_XLSX16 = 'd-mmm';
    const FORMAT_DATE_XLSX17 = 'mmm-yy';
    const FORMAT_DATE_XLSX22 = 'm/d/yy h:mm';
    const FORMAT_DATE_DATETIME = 'd/m/yy h:mm';
    const FORMAT_DATE_TIME1 = 'h:mm AM/PM';
    const FORMAT_DATE_TIME2 = 'h:mm:ss AM/PM';
    const FORMAT_DATE_TIME3 = 'h:mm';
    const FORMAT_DATE_TIME4 = 'h:mm:ss';
    const FORMAT_DATE_TIME5 = 'mm:ss';
    const FORMAT_DATE_TIME6 = 'h:mm:ss';
    const FORMAT_DATE_TIME7 = 'i:s.S';
    const FORMAT_DATE_TIME8 = 'h:mm:ss;@';
    const FORMAT_DATE_YYYYMMDDSLASH = 'yy/mm/dd;@';
    const FORMAT_CURRENCY_USD_SIMPLE = '"$"#,##0.00_-';
    const FORMAT_CURRENCY_USD = '$#,##0_-';
    const FORMAT_CURRENCY_EUR_SIMPLE = '#,##0.00_-"€"';
    const FORMAT_CURRENCY_EUR = '#,##0_-"€"';

    /**
     * @var string mode is an export mode or import mode. valid value are 'export' and 'import'.
     */
    public $mode = 'export';

    /**
     * @var boolean for set the export excel with multiple sheet.
     */
    public $isMultipleSection = false;

    /**
     * @var boolean Enables different header for odd and even pages.
     */
    public $evenAndOddHeaders = false;

    /**
     * @var array properties for set property on the excel object.
     */
    public $properties = [
        'creator' => 'My name',
        'company' => 'My factory',
        'title' => 'My title',
        'description' => 'My description',
        'category' => 'My category',
        'lastModifiedBy' => 'My name',
        'subject' => 'My subject',
        'keywords' => 'my, key, word',
    ];

    /**
     * @var int 创建时间
     */
    public $created_at;

    /**
     * @var int 修改时间
     */
    public $updated_at;

    /**
     * @var Model object or DataProvider object with much data.
     */
    public $model;

    /**
     * @var array columns to get the attributes from the model, this valid value only the exist attribute on the model.
     * If this is not set, then all attribute of the model will be set as columns.
     */
    public $columns = [];

    /**
     * @var array header to set the header column on first line. Set this if want to custom header.
     * If not set, the header will get attributes label of model attributes.
     */
    public $headers = [];

    /**
     * @var string|array name for file name to export or save.
     */
    public $fileName;

    /**
     * @var string save path is a directory to save the file or you can blank this to set the file as attachment.
     */
    public $savePath;

    /**
     * @var string format for excel to export. Valid value are 'Xls','Xlsx','Xml','Ods','Slk','Gnumeric','Csv', and 'Html'.
     */
    public $format;

    /**
     * @var boolean to set the title column on the first line.
     */
    public $setFirstTitle = true;

    /**
     * @var boolean to set the file excel to download mode.
     */
    public $asAttachment = false;

    /**
     * @var boolean to set the first record on excel file to a keys of array per line.
     * If you want to set the keys of record column with first record, if it not set, the header with use the alphabet column on excel.
     */
    public $setFirstRecordAsKeys = true;

    /**
     * @var boolean to set the sheet index by sheet name or array result if the sheet not only one.
     */
    public $setIndexSheetByName = false;

    /**
     * @var PhpWord sheetname to getting. This is only get the sheet with same name.
     */
    public $activeWord;

    /**
     * @var boolean to set the import data will return as array.
     */
    public $asArray;

    /**
     * @var array to unread record by index number.
     */
    public $leaveRecordByIndex = [];

    /**
     * @var array to read record by index, other will leave.
     */
    public $getOnlyRecordByIndex = [];

    /**
     * @var array|Formatter the formatter used to format model attribute values into displayable texts.
     * This can be either an instance of [[Formatter]] or an configuration array for creating the [[Formatter]]
     * instance. If this property is not set, the "formatter" application component will be used.
     */
    public $formatter;

    /**
     * @var boolean define the column autosize
     */
    public $autoSize = false;

    /**
     * @var boolean if true, this writer pre-calculates all formulas in the spreadsheet. This can be slow on large spreadsheets, and maybe even unwanted.
     */
    public $preCalculationFormula = false;

    /**
     * @var boolean Because of a bug in the Office2003 compatibility pack, there can be some small issues when opening Xlsx spreadsheets (mostly related to formula calculation)
     */
    public $compatibilityOffice2003 = false;

    /**
     * (non-PHPdoc)
     * @see \yii\base\Object::init()
     */
    public function init() {
        parent::init();
        if ($this->formatter == null) {
            $this->formatter = Yii::$app->getFormatter();
        } elseif (is_array($this->formatter)) {
            $this->formatter = Yii::createObject($this->formatter);
        }
        if (!$this->formatter instanceof Formatter) {
            throw new InvalidConfigException('The "formatter" property must be either a Format object or a configuration array.');
        }
        if (!$this->format) {
            $this->format = 'Word2007';
        }
        if (!$this->activeWord) {
            $this->activeWord = new PhpWord();
        }
    }

    /**
     * Formatter for i18n.
     * @return Formatter
     */
    public function formatter() {
        if (!isset($this->formatter)) {
            $this->formatter = Yii::$app->getFormatter();
        }

        return $this->formatter;
    }

    /**
     * Setting header to download generated file xls
     */
    public function setHeaders() {
        if ($this->format == 'Word2007') {
            header("Content-Description: File Transfer");
            header('Content-Disposition: attachment;filename="' . $this->getFileName() . '"');
            header('Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document');
            header('Content-Transfer-Encoding: binary');
            header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
            header('Expires: 0');
        }
    }

    /**
     * Getting the file name of exporting xls file
     * @return string
     */
    public function getFileName() {
        return WordIOFactory::buildFileName($this->fileName, $this->format);
    }

    /**
     * Setting properties for word file
     * @param PHPWord $objectWord
     * @param array $properties
     */
    public function properties(&$objectWord, $properties = []) {
        $docInfo = $objectWord->getDocInfo();
        foreach ($properties as $key => $value) {
            $keyname = "set" . ucfirst($key);
            $docInfo->{$keyname}($value);
        }
        $docInfo->setCreated($this->created_at ?: time());
        $docInfo->setModified($this->updated_at ?: time());
    }

    /**
     * 执行输出
     * @param PhpWord $objectWord
     * @param \common\logics\word\contract\Contract $model
     */
    public function executeRender(&$objectWord, $model = null) {
        if ($model == null) {
            if (!$this->model) {
                throw new InvalidConfigException('The "model" must be either a Format object or a configuration array.');
            }
            $model = $this->model;
        }


        $cover = $model->cover;
        if ($cover) {
            $section = $objectWord->addSection();
            $this->renderCover($section, $cover);
        }
        $toc = $model->toc;
        if ($toc) {
//            $sectionStyles = [
////                'headerHeight' => 50,
////                'footerHeight' => 50,
////                'borderTopColor' => '000000',
////                'borderTopSize' => 1,
//            ];
            $sectionStyles = null;
            $section = $objectWord->addSection($sectionStyles);
            $this->renderToc($section, $toc);

            $this->addHeader($section);
        }

        $documents = $model->documents;
        foreach ($documents as $doc) {
            $pageContent = $doc->toArray();
            $type = ArrayHelper::getValue($pageContent, 'type');
            //$renderMethod = 'renderPage' . ucfirst($type);
//            if ($this->hasMethod($renderMethod) && is_callable($renderMethod)) {
//                $this->{$renderMethod}($section, $pageContent);
//            }
            $section = $objectWord->addSection();
            if ($type == 'content') {


                $this->renderPage($section, $pageContent);
            } else {
                $this->renderAttach($section, $pageContent);
            }
        }
    }

    /**
     * Setting properties for word file
     * @param PhpWord $objectWord
     * @param array $styles
     */
    public function setStyles(&$objectWord, $styles = []) {
        //默认样式
        $objectWord->setDefaultFontName('仿宋');
        $objectWord->setDefaultFontSize(14);
        $objectWord->setDefaultParagraphStyle([
            'indentation' => [
                'firstLine' => Font::pointSizeToTwips(28),
                'left' => 0,
            ],
            'spacing' => Font::pointSizeToTwips(24),
            'spacingLineRule' => LineSpacingRule::EXACT,
        ]);

        //标题样式
        $headerTitleStyles = $this->getHeaderTitleStyles();
        foreach ($headerTitleStyles as $level => $style) {
            $objectWord->addTitleStyle($level, $style['fontStyle'], $style['paragraphStyle']);
        }

        //自定义样式
        $objectWord->addFontStyle('footerStyle', [
            'name' => '微软雅黑',
            'size' => 9,
            'color' => 'FF0000'
        ]);
    }

    public function getHeaderTitleStyles() {
        return [
            1 => [
                'fontStyle' => [
                    'name' => '华文中宋',
                    'size' => 28,
                    'bold' => true
                ],
                'paragraphStyle' => [
                    'indentation' => [
                        'firstLine' => 0,
                    ],
                    'spacing' => 1,
                    'spaceBefore' => Font::pointSizeToTwips(28),
                    'spaceAfter' => Font::pointSizeToTwips(28),
                    'alignment' => Jc::CENTER,
                    'textAlignment' => TextAlignment::CENTER,
                ]
            ],
            2 => [
                'fontStyle' => [
                    'name' => '华文中宋',
                    'size' => 16,
                    'bold' => true
                ],
                'paragraphStyle' => [
                    'indentation' => [
                        'firstLine' => 0,
                    ],
                    'spacing' => 1,
                    'spaceBefore' => Font::pointSizeToTwips(16),
                    'spaceAfter' => Font::pointSizeToTwips(16),
                    'alignment' => Jc::CENTER,
                    'textAlignment' => TextAlignment::CENTER,
                ]
            ],
        ];
    }

    public static function getListStyles() {

        return [
            [
                'font' => [
                    'name' => '黑体',
                    'size' => 14,
                ],
                'list' => [
                    'pStyle' => 'Heading3',
                    'format' => 'decimal', //bullet|decimal|upperRoman|lowerRoman|upperLetter|lowerLetter
                    'text' => '%1.', // Numbering level text e.g. %1 for nonbullet or bullet character.
                    'left' => 0, //缩进
                    'hanging' => 0, //悬挂缩进
//                    'hint' => 'eastAsia', //编号文本类型 Font content type, default, eastAsia, or cs.
                    'tabPos' => 0, //制表符位置
                    'suffix' => 'nothing', //Content between numbering symbol and paragraph text tab|space|nothing.
                    'alignment ' => \PhpOffice\PhpWord\SimpleType\Jc::BOTH,
                    'font' => '黑体',
//                    'restart' => true, //重新编号
//                    'start' => '', //编号
                ],
                'paragraph' => [
                    'indentation' => [
                        'firstLine' => Font::pointSizeToTwips(28),
                    ],
                    'spacing' => Font::pointSizeToTwips(24),
                    'spacingLineRule' => LineSpacingRule::EXACT,
                    'spaceBefore' => Font::pointSizeToTwips(14),
                    'spaceAfter' => Font::pointSizeToTwips(14),
                ]
            ],
            [
                'font' => null,
                'list' => [
                    'pStyle' => 'Heading4',
                    'format' => 'decimal', //bullet|decimal|upperRoman|lowerRoman|upperLetter|lowerLetter
                    'text' => '%1.%2.', // Numbering level text e.g. %1 for nonbullet or bullet character.
                    'left' => 0, //缩进
                    'hanging' => 0, //悬挂缩进
                    'tabPos' => 0, //制表符位置
                    'suffix' => 'nothing', //Content between numbering symbol and paragraph text tab|space|nothing.
                ],
                'paragraph' => [
                    'indentation' => [
                        'firstLine' => Font::pointSizeToTwips(28),
                    ],
                    'spacing' => Font::pointSizeToTwips(24),
                    'spacingLineRule' => LineSpacingRule::EXACT,
                ]
            ],
            [
                'font' => null,
                'list' => [
                    'pStyle' => 'Heading5',
                    'format' => 'decimal', //bullet|decimal|upperRoman|lowerRoman|upperLetter|lowerLetter
                    'text' => '%1.%2.%3.', // Numbering level text e.g. %1 for nonbullet or bullet character.
                    'left' => 0, //缩进
                    'hanging' => 0, //悬挂缩进
                    'tabPos' => 0, //制表符位置
                    'suffix' => 'nothing', //Content between numbering symbol and paragraph text tab|space|nothing.
                ],
                'paragraph' => [
                    'indentation' => [
                        'firstLine' => Font::pointSizeToTwips(28),
                    ],
                    'spacing' => Font::pointSizeToTwips(24),
                    'spacingLineRule' => LineSpacingRule::EXACT,
                ]
            ]
        ];
    }

    public static function getMultiLevelStyle($level = null, $name = null) {
        $listStyles = static::getListStyles();
        if ($level === null) {
            return ArrayHelper::getColumn($listStyles, 'list');
        }
        $styles = ArrayHelper::getValue($listStyles, $level);
        if ($name) {
            return ArrayHelper::getValue($styles, $name);
        }
        return $styles;
    }

    /**
     * 按层级列表输出
     * @param \PhpOffice\PhpWord\Element\Section $section
     * @param array $data
     * @param integer $listStyle
     * @param integer $level
     */
    public static function dumpItem(&$section, $data, $listStyle, $useNumberTitle = false, $level = 0) {

//        $objectWord->addNumberingStyle('hNum', array(
//            'type' => 'multilevel',
//            'levels' => array(
//                array('pStyle' => 'Heading4', 'format' => 'decimal', 'text' => '%1'),
//                array('pStyle' => 'Heading5', 'format' => 'decimal', 'text' => '%1.%2'),
//                array('pStyle' => 'Heading6', 'format' => 'decimal', 'text' => '%1.%2.%3'),
//            )
//        ));
//        $objectWord->addTitleStyle(4, array('size' => 16), array('numStyle' => 'hNum', 'numLevel' => 0));
//        $objectWord->addTitleStyle(5, array('size' => 14), array('numStyle' => 'hNum', 'numLevel' => 1));
//        $objectWord->addTitleStyle(6, array('size' => 12), array('numStyle' => 'hNum', 'numLevel' => 2));
//
//        $section->addTitle('标题1', 4);
//        $section->addTitle('标题2', 5);
//        $section->addTitle('标题3', 6);
//  $n = 1;
//    foreach ($lists as $list) {
//        // Hack to reset lists
//        $listFormat = $phpWord->addNumberingStyle(
//            'multilevel-'.$n,
//            array('type' => 'multilevel', 'levels' => array(
//                    array('format' => 'decimal', 'text' => '%1.', 'left' => 720, 'hanging' => 360, 'tabPos' => 720)                    
//                )
//            )
//        );
//        $section->addText(htmlspecialchars('Multilevel list.'));
//        $section->addListItem(htmlspecialchars('List Item I'), 0, null, 'multilevel-'.$n);
//        $section->addListItem(htmlspecialchars('List Item I.a'), 1, null, 'multilevel-'.$n);
//        $section->addListItem(htmlspecialchars('List Item I.b'), 1, null, 'multilevel-'.$n);
//        $section->addListItem(htmlspecialchars('List Item II'), 0, null, 'multilevel-'.$n);
//        $section->addListItem(htmlspecialchars('List Item II.a'), 1, null, 'multilevel-'.$n);
//        $section->addListItem(htmlspecialchars('List Item III'), 0, null, 'multilevel-'.$n);
//        $section->addTextBreak(2);
//        $n++;
//    }
        foreach ($data as $item) {
            $title = ArrayHelper::getValue($item, 'title', '');
            if (!$level && $useNumberTitle) {
                $section->addTitle($title, 3);
            } else {
                $styles = static::getMultiLevelStyle($level);
                $section->addListItem($title, $level, $styles['font'], $listStyle, $styles['paragraph']);
            }
            $content = ArrayHelper::getValue($item, 'content', '');
            $contentFormat = ArrayHelper::getValue($item, 'format');
            if ($content) {
                if ($contentFormat == 'html') {
                    PhpWordHtml::addHtml($section, $content, false, false);
                } else {
                    //$section->addText($content);
                    $section->addPreserveText($content);
                }
            }
            $items = ArrayHelper::getValue($item, 'items', []);
            if (empty($items)) {
                continue;
            }
            static::dumpItem($section, $items, $listStyle, $useNumberTitle, $level + 1);
        }
    }

    /**
     * 输出封面
     * @param \PhpOffice\PhpWord\Element\Section $section
     * @param array $data
     */
    public function renderCover(&$section, $data) {
        $title = ArrayHelper::getValue($data, 'title');
        $titleLevel = ArrayHelper::getValue($data, 'titleLevel');
        if ($title) {
            $section->addTitle($title, $titleLevel);
        }
        $columns = ArrayHelper::getValue($data, 'columns');
        DetailView::widget([
            'wordSection' => $section,
            'model' => $this->model,
            'view' => $this->activeWord,
            'attributes' => $columns,
            'rowHeight' => 36,
            'fontStyle' => [
                'name' => '华文中宋',
                'size' => 16,
                'bold' => true
            ],
            'paragraphStyle' => [
                'indentation' => [
                    'firstLine' => 0,
                ],
                'indent' => 0,
            ],
            'tableStyle' => [
                'position' => [
                    'tblpXSpec' => TablePosition::XALIGN_CENTER,
                    'tblpYSpec' => TablePosition::YALIGN_CENTER,
                ],
                'width' => 100
            ],
        ]);
    }

    /**
     * 输出目录
     * @param \PhpOffice\PhpWord\Element\Section $section
     * @param array $styles
     */
    public function renderToc(&$section, $styles = null) {
        // Add text elements
        $section->addTitle('目录', 2);
        // Add TOC #1

        $fontStyle = ['spaceAfter' => 60, 'size' => 12];
        $tocStyle = null;
        $minDepth = 2;
        $maxDepth = 4;
//        $toc = $section->addTOC($fontStyle12);

        $section->addTOC($fontStyle, $tocStyle, $minDepth, $maxDepth);
    }

    /**
     * 输出内容
     * @param \PhpOffice\PhpWord\Element\Section $section
     * @param array $name
     */
    public function registerNumberingStyle($section, $name = null) {
        $objectWord = $this->activeWord;
        if (!$name) {
            $id = $section->getSectionId();
            $name = 'MultiLevel' . $id;
        } else {
            $objectWord->addTitleStyle(3, [
                'name' => '黑体',
                'size' => 14,
            ], [
                'numStyle' => $name,
                'numLevel' => 0,
                'indentation' => [
                    'firstLine' => Font::pointSizeToTwips(28),
                ],
                'spacing' => Font::pointSizeToTwips(24),
                'spacingLineRule' => LineSpacingRule::EXACT,
                'spaceBefore' => Font::pointSizeToTwips(14),
                'spaceAfter' => Font::pointSizeToTwips(14),
            ]);
        }
        $levels = static::getMultiLevelStyle();
        $objectWord->addNumberingStyle($name, [
            'type' => 'multilevel',
            'levels' => $levels,
        ]);
        return $name;
    }

    /**
     * 输出内容
     * @param \PhpOffice\PhpWord\Element\Section $section
     * @param array $data
     */
    public function renderPage(&$section, $data) {
        $section->getStyle()->setPageNumberingStart(1);
        //添加页脚
        $this->addFooter($section);

        $listStyle = $this->registerNumberingStyle($section, 'NumerLevel');
        $title = ArrayHelper::getValue($data, 'title');
        $titleOptions = ArrayHelper::getValue($data, 'titleOptions');
        $titleLevel = ArrayHelper::getValue($titleOptions, 'level');
        if ($title) {
            $section->addTitle($title, $titleLevel);
        }
        $content = ArrayHelper::getValue($data, 'contents', []);
        static::dumpItem($section, $content, $listStyle, true);
    }

    /**
     * 添加页眉
     * @param \PhpOffice\PhpWord\Element\Section $section
     */
    public function addHeader(&$section) {
        $header = $section->addHeader();
        $pStyle = [
            'alignment' => \PhpOffice\PhpWord\SimpleType\Jc::RIGHT,
            'indentation' => [
                'firstLine' => 0,
                'left' => 0,
            ],
            'lineHeight' => 1,
            'spacingLineRule' => LineSpacingRule::AUTO,
            'suppressAutoHyphens' => true,
        ];
        $header->addText('济南云贝网络科技有限公司', [
            'name' => '幼圆',
            'size' => 8,
            'allCaps' => true,
        ], $pStyle);
        $header->addText('Jinan Youmbe Network Sci-Tech Co., Ltd.', [
            'name' => 'Calibri',
            'size' => 5.25,
            'allCaps' => true,
        ], ArrayHelper::merge($pStyle, [
            'borderBottomSize' => 20,
            'borderBottomStyle' => Style\Border::BORDER_STYLE_THIN_THICK_SMALL_GAP,
            'borderColor' => '000000'
        ]));
        $header->addImage('http://oss.youmbe.com/brand/word.png', [
            'width' => 72.85,
            'wrappingStyle' => \PhpOffice\PhpWord\Style\Image::WRAPPING_STYLE_INFRONT, //Wrapping style, inline, square, tight, behind, or infront.
            'alignment' => \PhpOffice\PhpWord\SimpleType\Jc::START,
            'positioning' => \PhpOffice\PhpWord\Style\Image::POSITION_RELATIVE,
//            'positioning' => \PhpOffice\PhpWord\Style\Image::POSITION_ABSOLUTE,
            'posHorizontal' => \PhpOffice\PhpWord\Style\Image::POSITION_HORIZONTAL_LEFT,
            'posHorizontalRel' => \PhpOffice\PhpWord\Style\Image::POS_RELTO_MARGIN,
            'top' => 37.2,
            'posVertical' => \PhpOffice\PhpWord\Style\Image::POSITION_ABSOLUTE,
            'posVerticalRel' => \PhpOffice\PhpWord\Style\Image::POS_RELTO_PAGE,
//            'posVertical' => \PhpOffice\PhpWord\Style\Image::POSITION_VERTICAL_BOTTOM,
//            'posVerticalRel' => \PhpOffice\PhpWord\Style\Image::POS_RELTO_TMARGIN,
        ]);


        if ($this->evenAndOddHeaders !== false) {
            $headerEven = $section->addHeader(\PhpOffice\PhpWord\Element\Header::EVEN);
            $tableEven = $headerEven->addTable();
            $tableEven->addRow();
            $tableEven->addCell(4500)->addImage('http://oss.youmbe.com/brand/word.png', [
                'width' => 80,
                'height' => 80,
                'alignment' => \PhpOffice\PhpWord\SimpleType\Jc::END
            ]);
            $tableEven->addCell(4500)->addText('This is the header');
        }
    }

    /**
     * 添加页脚
     * @param \PhpOffice\PhpWord\Element\Section $section
     */
    public function addFooter(&$section) {
        $section->createFooter()->addPreserveText('{PAGE}', [
            'name' => '微软雅黑',
            'size' => 9,
            'color' => 'FF0000'
        ], [
            'alignment' => \PhpOffice\PhpWord\SimpleType\Jc::CENTER
        ]);
        if ($this->evenAndOddHeaders !== false) {
            $section->addFooter(\PhpOffice\PhpWord\Element\Footer::EVEN)->addPreserveText('{PAGE}', [
                'name' => '微软雅黑',
                'size' => 9,
                'color' => 'FF0000'
            ], [
                'alignment' => \PhpOffice\PhpWord\SimpleType\Jc::CENTER
            ]);
        }
    }

    /**
     * 输出内容
     * @param \PhpOffice\PhpWord\Element\Section $section
     * @param array $data
     */
    public function renderAttach(&$section, $data) {
        $listStyle = $this->registerNumberingStyle($section);
        $title = ArrayHelper::getValue($data, 'title');
        $titleOptions = ArrayHelper::getValue($data, 'titleOptions');
        $titleLevel = ArrayHelper::getValue($titleOptions, 'level');
        if ($title) {
            $section->addTitle($title, $titleLevel);
        }
        $content = ArrayHelper::getValue($data, 'contents', []);

        static::dumpItem($section, $content, $listStyle);
    }

    /**
     * (non-PHPdoc)
     * @see \yii\base\Widget::run()
     */
    public function run222() {
        $phpWord = new PhpWord();

        $phpWord->setDefaultFontName('Calibri');
        $phpWord->setDefaultFontSize(14);

        $phpWord->addFontStyle('normal', array(
            'color' => '000000',
            'bold' => false,
            'italic' => false
        ));

        $phpWord->addFontStyle('title', array(
            'color' => '280099',
            'size' => 32,
            'bold' => true
        ));

        $phpWord->addFontStyle('identifier', array(
            'size' => 11,
            'bold' => true
        ));

        $phpWord->addFontStyle('destaque', array(
            'color' => '000000',
            'size' => 15,
            'bold' => true
        ));

        $phpWord->addFontStyle('assinatura', array(
            'size' => 11
        ));

        $phpWord->addFontStyle('abre-section', array(
            'color' => '280099',
            'size' => 19
        ));

        $phpWord->addParagraphStyle('p-abre-section', array(
            'align' => 'right',
            'borderBottomSize' => 2,
            'borderColor' => '000000'
        ));


        $section = $phpWord->addSection();
        $phpWord->addSection(['pageNumberingStart' => 0]);
        $header = $section->addHeader();
        $header->addText('Header Text', 'abre-section', 'p-abre-section');
        $footer = $section->addFooter();
        $footer->addPreserveText("Report footer \t\t\t\t\t\t\t\t\t {PAGE}", 'assinatura', array('align' => 'justify'));

        $section->addText('Special Text', 'abre-section', 'p-abre-section');


        //导出word
        $this->writeFile($phpWord);
        unset($phpWord);
    }

    /**
     * (non-PHPdoc)
     * @see \yii\base\Widget::run()
     */
    public function run() {
        $phpWord = $this->activeWord;
        $phpWord->getSettings()->setEvenAndOddHeaders($this->evenAndOddHeaders);
        if (isset($this->properties)) {
            $this->properties($phpWord, $this->properties);
        }
        //设置文档使用样式
        $this->setStyles($phpWord);
        //执行输出
        $this->executeRender($phpWord);
        //导出word
        $this->writeFile($phpWord);
        unset($phpWord);
    }

    /**
     * saving the xls file to download or to path
     * @param PhpWord $word
     */
    public function writeFile($word) {
        if (!isset($this->format)) {
            $this->format = 'Word2007';
        }
        if (!isset($this->format)) {
            $this->format = 'Word2007';
        }
        $filename = $this->getFileName();
        if ($this->asAttachment) {
            $word->save($filename, $this->format, true);
        } else {
            if (isset($this->savePath) && $this->savePath != null) {
                $path = $this->savePath . '/' . $filename;
                $objectwriter = WordIOFactory::createWriter($word, $this->format);
                $objectwriter->save($path);
            } else {
                $word->save($filename, $this->format, false);
            }
        }
        exit();
    }

    public function renderCustom(&$section, $data) {
        $section->addTitle('技术服务合同', 1);
        // Adding Text element to the Section having font styled by default...
        $section->addText('"Learn from yesterday, live for today, hope for tomorrow. '
        . 'The important thing is not to stop questioning." '
        . '(Albert Einstein)');
        $this->executeList($section);
    }

    public function renderDemo(&$section, $data = null) {

        $txt = '1111';
        $depth = 0;
        $font = null;
        $list = new \PhpOffice\PhpWord\Style\ListItem();
        $list->setListType(1);
        $para = null;

        $section->addListItem($txt, $depth, $font, $list, $para);

        /* Note: any element you append to a document must reside inside of a Section. */

        // Adding an empty Section to the document...
        $section = $phpWord->addSection();
        // Adding Text element to the Section having font styled by default...
        $section->addText('"Learn from yesterday, live for today, hope for tomorrow. ' . 'The important thing is not to stop questioning." ' . '(Albert Einstein)');

        /*
         * Note: it's possible to customize font style of the Text element you add in three ways:
         * - inline;
         * - using named font style (new font style object will be implicitly created);
         * - using explicitly created font style object.
         */
        // Adding Text element with font customized inline...
        $section->addText('"Great achievement is usually born of great sacrifice, ' . 'and is never the result of selfishness." ' . '(Napoleon Hill)', [
            'name' => 'Tahoma',
            'size' => 10
        ]);

        // Adding Text element with font customized using named font style...
        $fontStyleName = 'oneUserDefinedStyle';
        $phpWord->addFontStyle($fontStyleName, [
            'name' => 'Tahoma',
            'size' => 10,
            'color' => '1B2232',
            'bold' => true
        ]);
        $section->addText('"The greatest accomplishment is not in never falling, ' . 'but in rising again after you fall." ' . '(Vince Lombardi)', $fontStyleName);

        // Adding Text element with font customized using explicitly created font style object...
        $fontStyle = new \PhpOffice\PhpWord\Style\Font();
        $fontStyle->setBold(true);
        $fontStyle->setName('Tahoma');
        $fontStyle->setSize(13);
        $myTextElement = $section->addText('"Believe you can and you\'re halfway there." (Theodor Roosevelt)');
        $myTextElement->setFontStyle($fontStyle);
    }

    public function demolist($section) {
        //添加列表元素
        $section->addListItem('ListItem 1', 0);
        $section->addListItem('ListItem 2', 0);
        $section->addListItem('ListItem 3', 0);
        $section->addTextBreak(2);

        $section->addListItem('ListItem 1', 0);
        $section->addListItem('ListItem 1.1', 1);
        $section->addListItem('ListItem 1.2', 1);
        $section->addListItem('ListItem 1.3(styled)', 1, ['bold' => true]);
        $section->addListItem('ListItem 1.3.1', 2);
        $section->addListItem('ListItem 1.3.2', 2);
        $section->addTextBreak(2);

        $listStyle = ['listType' => ListItemStyle::TYPE_NUMBER];
        $section->addListItem('ListItem 1', 0, null, $listStyle);
        $section->addListItem('ListItem 2', 0, null, $listStyle);
        $section->addListItem('ListItem 3', 0, null, $listStyle);
        $section->addTextBreak(2);


        $listStyle = ['listType' => ListItemStyle::TYPE_NUMBER_NESTED];
        $section->addListItem('ListItem 1', 0, 'myOwnStyle', $listStyle, 'P-Style');
        $section->addListItem('ListItem 2', 0, 'myOwnStyle', $listStyle, 'P-Style');
        $section->addListItem('ListItem 3', 1, 'myOwnStyle', $listStyle, 'P-Style');
        $section->addListItem('ListItem 4', 1, 'myOwnStyle', $listStyle, 'P-Style');
        $section->addListItem('ListItem 5', 2, 'myOwnStyle', $listStyle, 'P-Style');
        $section->addListItem('ListItem 6', 2, 'myOwnStyle', $listStyle, 'P-Style');
        $section->addListItem('ListItem 7', 1, 'myOwnStyle', $listStyle, 'P-Style');
        $section->addListItem('ListItem 8', 0, 'myOwnStyle', $listStyle, 'P-Style');
    }

    /**
     * Setting label or keys on every record if setFirstRecordAsKeys is true.
     * @param array $sheetData
     * @return multitype:multitype:array
     */
    public function executeArrayLabel($sheetData) {
        $keys = ArrayHelper::remove($sheetData, '1');

        $new_data = [];

        foreach ($sheetData as $values) {
            $new_data[] = array_combine($keys, $values);
        }

        return $new_data;
    }

    /**
     * Leave record with same index number.
     * @param array $sheetData
     * @param array $index
     * @return array
     */
    public function executeLeaveRecords($sheetData = [], $index = []) {
        foreach ($sheetData as $key => $data) {
            if (in_array($key, $index)) {
                unset($sheetData[$key]);
            }
        }
        return $sheetData;
    }

    /**
     * Read record with same index number.
     * @param array $sheetData
     * @param array $index
     * @return array
     */
    public function executeGetOnlyRecords($sheetData = [], $index = []) {
        foreach ($sheetData as $key => $data) {
            if (!in_array($key, $index)) {
                unset($sheetData[$key]);
            }
        }
        return $sheetData;
    }

    /**
     * Getting column value.
     * @param Model $model
     * @param array $params
     * @return Ambigous <NULL, string, mixed>
     */
    public function executeGetColumnData($model, $params = []) {
        $value = null;
        if (isset($params['value']) && $params['value'] !== null) {
            if (is_string($params['value'])) {
                $value = ArrayHelper::getValue($model, $params['value']);
            } else {
                $value = call_user_func($params['value'], $model, $this);
            }
        } elseif (isset($params['attribute']) && $params['attribute'] !== null) {
            $value = ArrayHelper::getValue($model, $params['attribute']);
        }

        if (isset($params['format']) && $params['format'] != null)
            $value = $this->formatter()->format($value, $params['format']);

        return $value;
    }

    /**
     * Populating columns for checking the column is string or array. if is string this will be checking have a formatter or header.
     * @param array $columns
     * @throws InvalidParamException
     * @return multitype:multitype:array
     */
    public function populateColumns($columns = []) {
        $_columns = [];
        foreach ($columns as $key => $value) {
            if (is_string($value)) {
                $value_log = explode(':', $value);
                $_columns[$key] = ['attribute' => $value_log[0]];

                if (isset($value_log[1]) && $value_log[1] !== null) {
                    $_columns[$key]['format'] = $value_log[1];
                }

                if (isset($value_log[2]) && $value_log[2] !== null) {
                    $_columns[$key]['header'] = $value_log[2];
                }
            } elseif (is_array($value)) {
                if (!isset($value['attribute']) && !isset($value['value'])) {
                    throw new \InvalidArgumentException('Attribute or Value must be defined.');
                }
                $_columns[$key] = $value;
            }
        }

        return $_columns;
    }

    /**
     * (non-PHPdoc)
     * @see \yii\base\Widget::run()
     */
    public function run2() {
        $sheet = new Spreadsheet();

        if (!isset($this->models)) {
            throw new InvalidConfigException('Config models must be set');
        }

        if (isset($this->properties)) {
            $this->properties($sheet, $this->properties);
        }


        if ($this->isMultipleSection) {
            $index = 0;
            $worksheet = [];
            foreach ($this->models as $title => $models) {
                $sheet->createSection($index);
                $sheet->getSheet($index)->setTitle($title);
                $worksheet[$index] = $sheet->getSheet($index);
                $columns = isset($this->columns[$title]) ? $this->columns[$title] : [];
                $headers = isset($this->headers[$title]) ? $this->headers[$title] : [];
                $this->executeColumns($worksheet[$index], $models, $this->populateColumns($columns), $headers);
                $index++;
            }
        } else {
            $worksheet = $sheet->getActiveSheet();
            $this->executeColumns($worksheet, $this->models, isset($this->columns) ? $this->populateColumns($this->columns) : [], isset($this->headers) ? $this->headers : []);
        }

        if ($this->asAttachment) {
            $this->setHeaders();
        }
        $this->writeFile($sheet);
        $sheet->disconnectWorksheets();
        unset($sheet);
    }

}
